package com.dwy2002.course.impl;


import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.dwy2002.common.Result;
import com.dwy2002.common.utils.JwtUtil;
import com.dwy2002.common.utils.OssUtils;
import com.dwy2002.common.utils.Validator;
import com.dwy2002.course.mapper.CourseEnrollmentsMapper;
import com.dwy2002.course.mapper.CourseImagesMapper;
import com.dwy2002.course.mapper.CoursesMapper;
import com.dwy2002.course.service.ICourseEnrollmentService;
import com.dwy2002.course.service.ICourseImagesService;
import com.dwy2002.course.service.ICoursesService;
import com.dwy2002.enums.Status;
import com.dwy2002.pojo.dto.courses.CourseImagesDTO;
import com.dwy2002.pojo.dto.courses.CoursesDTO;
import com.dwy2002.pojo.dto.courses.CoursesPageDTO;
import com.dwy2002.pojo.po.courses.CourseEnrollments;
import com.dwy2002.pojo.po.courses.CourseImages;
import com.dwy2002.pojo.po.courses.Courses;
import com.dwy2002.pojo.po.users.Users;
import com.dwy2002.pojo.vo.courses.CourseImageVO;
import com.dwy2002.pojo.vo.courses.CoursesVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

import static com.dwy2002.common.Result.*;
import static com.dwy2002.common.utils.Utils.*;

/**
 * @author 杜伟毅
 * @version 2.0
 * @since 2025/01/21
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CoursesServiceImpl extends ServiceImpl<CoursesMapper, Courses> implements ICoursesService {
    private final JwtUtil jwtUtil;
    private final CourseEnrollmentsMapper courseEnrollmentsMapper;
    private final CourseImagesMapper imagesMapper;
    private final ICourseImagesService imagesService;
    private final ICourseEnrollmentService enrollmentService;
    private final OssUtils ossUtils;
    private final ReentrantLock lock = new ReentrantLock();


    /**
     * 创建课程
     *
     * @param courses 从前端接收的创建课程信息
     * @author 杜伟毅
     * @since 2025/1/21 0021 0:02
     */
    @Override
    public Result<Void> insertCourses(CoursesDTO courses) {
        Validator.of(courses)
                .notEmpty(CoursesDTO::getTitle, "标题不能为空!")
                .notZeroDecimal(CoursesDTO::getFee, "价格不能为0或空")
                .notNull(CoursesDTO::getStatus, "状态不能为空!")
                .inRange(CoursesDTO::getStatus, 3, 0, "状态错误，没有这个状态选项!")
                .notNull(CoursesDTO::getStartTime, "课程开始时间不能为空")
                .notNull(CoursesDTO::getPeopleLimit, "课程限制人数不能为空")
                .minValue(CoursesDTO::getPeopleLimit, -1, "数值不规范，请输入大于或等于-1以上的数")
                .mustSatisfy(ObjectUtils.isEmpty(courses.getCourseImages()) && courses.getCourseImages().size() > 5, "图片数量不能超过5张")
                .validate();
        String courseId = IdWorker.get32UUID();
        Courses course = BeanUtil.copyProperties(courses, Courses.class);
        course.setId(courseId);
        boolean save = save(course);

        //保存图片
        saveCourseImages(courses.getCourseImages(), courseId);

        return saveResult(save, "课程创建成功", "课程创建失败");
    }

    /**
     * 批量删除课程
     *
     * @param ids 课程ID集合
     * @author 杜伟毅
     * @since 2025/2/7 0007 21:22
     */
    @Override
    public Result<Void> deleteListCourses(List<String> ids) {
        if (ids.isEmpty()) {
            return fail(Status.BAD_REQUEST, "ID不能为空");
        }

        LambdaQueryWrapper<Courses> lqw = new LambdaQueryWrapper<>();
        lqw.in(Courses::getId, ids);
        List<Courses> courses = list(lqw);
        //经过数据库验证的id集合
        List<String> validIds = courses.stream()
                .filter(Objects::nonNull)
                .map(Courses::getId)
                .toList();

        //删除课程封面
        courses.stream()
                .map(Courses::getImage)
                .forEach(ossUtils::deleteImage);

        //删除课程图片
        imagesService.deleteBathImage(validIds);
        //删除报名关系
        enrollmentService.deleteEnrollment(validIds);
        //删除课程
        boolean remove = removeByIds(validIds);
        return handleResult(remove, "删除成功", "删除失败");
    }

    /**
     * 分页查询
     * <p>
     * 查询：可按标题搜索
     * <p>
     * 排序：可按课程开始时间、课程创建时间、课程修改时间进行升序和降序排序。
     * <p>
     * 分类：可按课程状态进行分类
     *
     * @param dto 课程类的分页数据类
     * @author 杜伟毅
     * @since 2025/1/20 0020 23:45
     */
    @Override
    public Result<Page<Courses>> getCoursesPage(CoursesPageDTO dto) {
        Validator.of(dto)
                .mustSatisfy(isPageNull(dto.getPageNo(), dto.getPageSize()), "请传入分页数据")
                .mustSatisfy(isPageZero(dto.getPageNo(), dto.getPageSize()), "分页数据不能为0")
                .validate();
        LambdaQueryWrapper<Courses> lqw = new LambdaQueryWrapper<>();
        //标题模糊搜索
        lqw.like(!isEmptyOrNull(dto.getTitle()), Courses::getTitle, dto.getTitle())
                //按照开课时间排序
                .orderBy(dto.getStartTimeEnabled(), dto.getStartTimeIsAsc(), Courses::getStartTime)
                //按照课程创建时间排序
                .orderBy(dto.getCreatedEnabled(), dto.getCreatedIsAsc(), Courses::getCreatedAt)
                //按照课程修改时间排序
                .orderBy(dto.getUpdatedEnabled(), dto.getUpdatedIsAsc(), Courses::getUpdatedAt)
                //按照课程费用排序
                .orderBy(dto.getFeeEnabled(), dto.getFeeIsAsc(), Courses::getFee)
                //根据statusFilterEnabled状态,查询某个状态的课程
                .eq(dto.getStatusFilterEnabled(), Courses::getStatus, dto.getStatus());
        //分页
        Page<Courses> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        Page<Courses> coursesPage = this.page(page, lqw);

        return success(coursesPage);
    }

    /**
     * 修改课程
     *
     * @param dto 从前端接收的修改课程信息
     * @author 杜伟毅
     * @since 2025/1/21 0021 0:04
     */
    @Override
    public Result<Void> updateCourses(CoursesDTO dto) {
        Validator.of(dto)
                .notEmpty(CoursesDTO::getId, "课程ID不能为空!")
                .notEmpty(CoursesDTO::getTitle, "标题不能为空!")
                .notZeroDecimal(CoursesDTO::getFee, "价格不能为0或空!")
                .notNull(CoursesDTO::getStatus, "状态不能为空!")
                .inRange(CoursesDTO::getStatus, 5, 0, "状态错误，没有这个状态选项!")
                .notNull(CoursesDTO::getStartTime, "课程开始时间不能为空!")
                .minValue(CoursesDTO::getPeopleLimit, -1, "数值不规范，请输入大于或等于-1以上的数")
                .mustSatisfy(ObjectUtils.isEmpty(dto.getCourseImages()) && dto.getCourseImages().size() > 5, "图片数量不能超过5张")
                .validate(); // 自动抛异常

        Courses course = getById(dto.getId());
        if (ObjectUtils.isEmpty(course)) {
            return fail(Status.BAD_REQUEST, "课程不存在");
        }

        LambdaUpdateWrapper<Courses> luw = new LambdaUpdateWrapper<>();
        luw.eq(Courses::getId, dto.getId())
                .set(Courses::getTitle, dto.getTitle())
                .set(!isEmptyOrNull(dto.getDescription()), Courses::getDescription, dto.getDescription())
                .set(!isEmptyOrNull(dto.getImage()), Courses::getImage, dto.getImage())
                .set(Objects.nonNull(dto.getFee()), Courses::getFee, dto.getFee())
                .set(Objects.nonNull(dto.getStartTime()), Courses::getStartTime, dto.getStartTime())
                .set(Objects.nonNull(dto.getStatus()), Courses::getStatus, dto.getStatus())
                .set(Objects.nonNull(dto.getPeopleLimit()), Courses::getPeopleLimit, dto.getPeopleLimit());
        boolean update = update(luw);
        //保存图片
        saveCourseImages(dto.getCourseImages(), dto.getId());

        /*
        删除旧的封面图片
         */
        // 更安全的删除旧封面图片逻辑
        if (!StringUtils.equals(course.getImage(), dto.getImage())
                && !StringUtils.isEmpty(course.getImage())) {
            try {
                // 确认新图片已存在再删除旧图片
                if (ossUtils.isImageExist(dto.getImage())) {
                    ossUtils.deleteImage(course.getImage());
                }
            } catch (Exception e) {
                log.error("删除旧封面图片失败: {}", course.getImage(), e);
            }
        }

        return handleResult(update, "课程修改成功", "课程修改失败");
    }

    /**
     * 报名课程
     *
     * @param id String 课程的ID
     * @author 杜伟毅
     * @since 2025/1/22 0022 23:32
     */
    @Transactional
    @Override
    public Result<Void> joinCourse(String id) {

        if (isEmptyOrNull(id)) {
            return fail(Status.BAD_REQUEST, "id不能为空");
        }

        lock.lock();
        try {
            Courses course = getById(id);

            if (course == null) {
                return fail(Status.BAD_REQUEST, "课程不存在");
            }

            String userId = jwtUtil.parseToken(getToken(), Users.class).getId();

            if (isEnrolled(id)) {
                return fail(Status.BAD_REQUEST, "你已重复报名，请勿重复报名!");
            }

            if (!equalsCourseLimit(course.getEnrolledCount(), course.getPeopleLimit())) {
                return fail(Status.BAD_REQUEST, "课程人数已满，课程报名失败");
            }

            /*
            将该课程人数+1
             */
            LambdaUpdateWrapper<Courses> luw = new LambdaUpdateWrapper<>();
            luw.eq(Courses::getId, id)
                    .setIncrBy(equalsCourseLimit(course.getEnrolledCount(), course.getPeopleLimit()), Courses::getEnrolledCount, 1);
            boolean update = update(luw);

            //将课程添加到我的课程表
            boolean enrolled = enrollCourse(course.getId(), userId);

            return saveResult(update || enrolled, "课程报名成功", "课程报名失败");
        } finally {
            lock.unlock();
        }
    }

    /**
     * 取消报名课程
     *
     * @param id String 课程的ID
     * @author 杜伟毅
     * @since 2025/1/22 0022 23:33
     */
    @Transactional
    @Override
    public Result<Void> leaveCourse(String id) {
        if (isEmptyOrNull(id)) {
            return fail(Status.BAD_REQUEST, "id不能为空");
        }

        lock.lock();
        try {
            //查询课程的信息
            Courses course = getOne(new LambdaQueryWrapper<Courses>().eq(Courses::getId, id));

            /*
              检查用户是否已经报名了某个课程
             */
            if (!isEnrolled(id)) {
                return fail(Status.BAD_REQUEST, "不好意思，您未报名该课程");
            }

            if (course == null) {
                return fail(Status.BAD_REQUEST, "课程不存在");
            }

            /*
            将该课程人数-1
             */
            LambdaUpdateWrapper<Courses> luw = new LambdaUpdateWrapper<>();
            luw.eq(Courses::getId, id)
                    .setDecrBy(course.getEnrolledCount() > 0, Courses::getEnrolledCount, 1);
            boolean update = update(luw);

            //删除退出的课程
            boolean deleteMyCourse = deleteMyCourse(course.getId());

            return saveResult(update || deleteMyCourse, "取消报名课程成功", "取消报名课程失败");
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取课程详情
     *
     * @param id String 课程的ID
     * @author 杜伟毅
     * @since 2025/1/24 0024 17:51
     */
    @Override
    public Result<CoursesVO> getCourseDetails(String id) {
        if (isEmptyOrNull(id)) {
            return fail(Status.BAD_REQUEST, "id不能为空");
        }

        Courses course = getById(id);
        if (ObjectUtils.isEmpty(course)) {
            return fail(Status.BAD_REQUEST, "课程不存在");
        }
        LambdaQueryWrapper<CourseImages> lqw = new LambdaQueryWrapper<CourseImages>().eq(CourseImages::getCourseId, id);
        CoursesVO coursesVO = BeanUtil.copyProperties(course, CoursesVO.class);
        List<CourseImageVO> courseImageVOS = BeanUtil.copyToList(imagesMapper.selectList(lqw), CourseImageVO.class);
        coursesVO.setCourseImages(courseImageVOS);
        return success(coursesVO);
    }

    //提取出来的方法-------------------------------------

    /**
     * 检查某个用户是否已经报名了某个课程
     *
     * @param courseId String 课程ID
     * @return true 该用户已报名该课程，false该用户未报名该课程
     * @author 杜伟毅
     * @since 2025/1/23 0023 22:10
     */
    private boolean isEnrolled(String courseId) {
        String userId = jwtUtil.parseToken(getToken(), Users.class).getId();
        LambdaQueryWrapper<CourseEnrollments> lqw = new LambdaQueryWrapper<>();
        lqw.eq(CourseEnrollments::getCourseId, courseId)
                .eq(CourseEnrollments::getUserId, userId);
        return courseEnrollmentsMapper.selectOne(lqw) != null;
    }

    /**
     * 将信息保存到我的课程表
     *
     * @param courseId String 课程ID
     * @param userId   String 用户ID
     * @return true 添加成功，false 添加失败
     * @author 杜伟毅
     * @since 2025/1/23 0023 22:17
     */
    private boolean enrollCourse(String courseId, String userId) {
        CourseEnrollments courseEnrollment = new CourseEnrollments();
        courseEnrollment.setCourseId(courseId);
        courseEnrollment.setUserId(userId);
        return Db.save(courseEnrollment);
    }

    /**
     * 删除我的课程
     *
     * @param courseId String 课程ID
     * @return true 删除成功，false 删除失败
     * @author 杜伟毅
     * @since 2025/1/23 0023 22:46
     */
    private boolean deleteMyCourse(String courseId) {
        String userId = jwtUtil.parseToken(getToken(), Users.class).getId();
        LambdaQueryWrapper<CourseEnrollments> lqw = new LambdaQueryWrapper<>();
        lqw.eq(!isEmptyOrNull(courseId), CourseEnrollments::getCourseId, courseId)
                .eq(!isEmptyOrNull(userId), CourseEnrollments::getUserId, userId);
        return courseEnrollmentsMapper.delete(lqw) > 0;
    }

    //----------------------------------------------------------------------------------

    /**
     * 提取方法，保存课程图片
     *
     * @param images   图片链接集合
     * @param courseId 课程id
     * @author 杜伟毅
     * @since 2025/2/28 0028 23:06
     */
    private void saveCourseImages(List<CourseImagesDTO> images, String courseId) {
        for (CourseImagesDTO image : images) {
            image.setCourseId(courseId);
            if (!imagesService.saveCourseImages(image)) {
                continue;
            }
        }
    }
}



/*
2.0优化：
修改原先大段的重复的if-return格式的代码，采用 "校验链 + 统一异常处理" 模式，结合策略模式与函数式编程思想，实现以下优化：
1.校验逻辑集中管理
2.错误信息统一收集
3.代码行数减少50%以上
4.支持单次返回多个错误
 */